package org.dromara.web.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.dromara.common.core.config.UserLoginConfig;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.domain.dto.RoleDTO;
import org.dromara.common.core.domain.model.WxLoginUser;
import org.dromara.common.core.domain.model.XcxLoginBody;
import org.dromara.common.core.enums.LoginType;
import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.*;
import org.dromara.common.core.utils.spring.SpringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.log.event.LogininforEvent;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.system.service.ISysPermissionService;
import org.dromara.web.client.HttpWxService;
import org.dromara.web.client.config.WxAppConfig;
import org.dromara.wx.domain.vo.WxUserVo;
import org.dromara.wx.service.IWxUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.function.Supplier;

@RequiredArgsConstructor
@Slf4j
@Service
public class WxLoginService {



    @Autowired
    private UserLoginConfig userLoginConfig;

    @Autowired
    private WxAppConfig wxAppConfig;

    @Resource
    private HttpWxService httpWxService;

    private final IWxUserService xhljIWxUserService;
    private final ISysPermissionService permissionService;

    public WxUserVo login(XcxLoginBody loginBody) {
        ValidatorUtils.validate(loginBody);
        // xcxCode 为 小程序调用 wx.login 授权后获取
        String xcxCode = loginBody.getXcxCode();
        String appid= loginBody.getAppid();
        String secret= loginBody.getSecret();

        String result=httpWxService.getResult(wxAppConfig.getAddress(),xcxCode,appid,secret,wxAppConfig.getGrantType());
        String openid=getOpenid(result);

        // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可
        WxUserVo user = loadWxUserByOpenid(openid);
        return user;
    }

    private WxUserVo loadWxUserByOpenid(String openid) {

        // 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户
        // todo 自行实现 userService.selectUserByOpenid(openid);
        WxUserVo user = xhljIWxUserService.selectWxUserByOpenId(openid);
        if (ObjectUtils.isEmpty(user)) {
            log.info("小程序登录用户 openid：{} 不存在.", openid);
            log.info("开始创建默认微信用户 openid：{} ", openid);
            user =  xhljIWxUserService.createDefaultVo(openid);
            if(ObjectUtils.isNotEmpty(user)){
                log.info("创建默认微信用户 openid：{} 成功", openid);
            }else {
                throw new UserException("创建默认微信用户 openid：{} 失败", openid);
            }
        }
        return user;
    }
    private String getOpenid(String result) {
        String openid = null;
        if(StringUtils.isNotEmpty(result)){
            JSONObject jsonObject = JSON.parseObject(result);
            Object openidObj = jsonObject.get("openid");
            if(ObjectUtil.isNotNull(openidObj)){
                openid = openidObj.toString();
            }
            else{
                log.error("登录微信用户 result：{} code过期或错误code", result);
                throw new UserException("登录微信用户 code过期或错误code");
            }
        }else {
            log.error("请求地址异常 => {} ", wxAppConfig.toString());
            throw new UserException("请求微信地址异常");
        }
        return openid;
    }
    public WxLoginUser buildWxLoginUser(WxUserVo user) {
        WxLoginUser wxloginUser = new WxLoginUser();
        wxloginUser.setUserId(user.getId().longValue());
        wxloginUser.setUsername(user.getUserName());
        wxloginUser.setNickname(user.getNickname());
        wxloginUser.setUserType(user.getUserType());
        // SAIL 解决LoginUser用户类型不能为空
        wxloginUser.setUserType(user.getUserType());
        wxloginUser.setRolePermission(permissionService.getRolePermission(Long.valueOf(user.getId())));
        log.info("用户权限信息 => {} ", JsonUtils.toJsonString(wxloginUser.getRolePermission()));
        List<RoleDTO> roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class);
        wxloginUser.setRoles(roles);

        return wxloginUser;
    }


    /**
     * 登录校验
     */
    public void checkLogin(LoginType loginType, String username, Supplier<Boolean> supplier) {
        String errorKey = GlobalConstants.PWD_ERR_CNT_KEY + username;
        String loginFail = Constants.LOGIN_FAIL;

        // 获取用户登录错误次数，默认为0 (可自定义限制策略 例如: key + username + ip)
        int errorNumber = ObjectUtil.defaultIfNull(RedisUtils.getObject(errorKey), 0);
        // 锁定时间内登录 则踢出
        if (errorNumber >=  userLoginConfig.getMaxRetryCount()) {
            recordLogininfor( username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(),  userLoginConfig.getMaxRetryCount(),  userLoginConfig.getLockTime()));
            throw new UserException(loginType.getRetryLimitExceed(),  userLoginConfig.getMaxRetryCount(),  userLoginConfig.getLockTime());
        }

        if (supplier.get()) {
            // 错误次数递增
            errorNumber++;
            RedisUtils.setObject(errorKey, errorNumber, userLoginConfig.getLockTime());
            // 达到规定错误次数 则锁定登录
            if (errorNumber >=  userLoginConfig.getMaxRetryCount()) {
                recordLogininfor( username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(),  userLoginConfig.getMaxRetryCount(),  userLoginConfig.getLockTime()));
                throw new UserException(loginType.getRetryLimitExceed(),  userLoginConfig.getMaxRetryCount(),  userLoginConfig.getLockTime());
            } else {
                // 未达到规定错误次数
                recordLogininfor( username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber));
                throw new UserException(loginType.getRetryLimitCount(), errorNumber);
            }
        }

        // 登录成功 清空错误次数
        RedisUtils.deleteObject(errorKey);
    }

    /**
     * 记录登录信息
     *
     *
     * @param username 用户名
     * @param status   状态
     * @param message  消息内容
     */
    public void recordLogininfor(String username, String status, String message) {
        LogininforEvent logininforEvent = new LogininforEvent();
        logininforEvent.setUsername(username);
        logininforEvent.setStatus(status);
        logininforEvent.setMessage(message);
//        logininforEvent.setRequest(ServletUtils.getRequest());
        SpringUtils.context().publishEvent(logininforEvent);
    }
}
